// filesys.cc 
//	Routines to manage the overall operation of the file system.
//	Implements routines to map from textual file names to files.
//
//	Each file in the file system has:
//	   A file header, stored in a sector on disk 
//		(the size of the file header data structure is arranged
//		to be precisely the size of 1 disk sector)
//	   A number of data blocks
//	   An entry in the file system directory
//
// 	The file system consists of several data structures:
//	   A bitmap of free disk sectors (cf. bitmap.h)
//	   A directory of file names and file headers
//
//      Both the bitmap and the directory are represented as normal
//	files.  Their file headers are located in specific sectors
//	(sector 0 and sector 1), so that the file system can find them 
//	on bootup.
//
//	The file system assumes that the bitmap and directory files are
//	kept "open" continuously while Nachos is running.
//
//	For those operations (such as Create, Remove) that modify the
//	directory and/or bitmap, if the operation succeeds, the changes
//	are written immediately back to disk (the two files are kept
//	open during all this time).  If the operation fails, and we have
//	modified part of the directory and/or bitmap, we simply discard
//	the changed version, without writing it back to disk.
//
// 	Our implementation at this point has the following restrictions:
//
//	   there is no synchronization for concurrent accesses
//	   files have a fixed size, set when the file is created
//	   files cannot be bigger than about 3KB in size
//	   there is no hierarchical directory structure, and only a limited
//	     number of files can be added to the system
//	   there is no attempt to make the system robust to failures
//	    (if Nachos exits in the middle of an operation that modifies
//	    the file system, it may corrupt the disk)
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"

#include "disk.h"
#include "directory.h"
#include "filehdr.h"
#include "filesys.h"

// Sectors containing the file headers for the bitmap of free sectors,
// and the directory of files.  These file headers are placed in well-known 
// sectors, so that they can be located on boot-up.
#define FreeMapSector 		0
#define DirectorySector 	1

// Initial file sizes for the bitmap and directory; until the file system
// supports extensible files, the directory size sets the maximum number 
// of files that can be loaded onto the disk.
#define FreeMapFileSize 	(NumSectors / BitsInByte)
#define NumDirEntries 		10
#define DirectoryFileSize 	(sizeof(DirectoryEntry) * NumDirEntries)

//----------------------------------------------------------------------
// FileSystem::FileSystem
// 	Initialize the file system.  If format = TRUE, the disk has
//	nothing on it, and we need to initialize the disk to contain
//	an empty directory, and a bitmap of free sectors (with almost but
//	not all of the sectors marked as free).  
//
//	If format = FALSE, we just have to open the files
//	representing the bitmap and the directory.
//
//	"format" -- should we initialize the disk?
//----------------------------------------------------------------------

FileSystem::FileSystem(bool format)
{ 
    DEBUG('f', "Initializing the file system.\n");
    if (format) {
        BitMap *freeMap = new BitMap(NumSectors);
        Directory *directory = new Directory(NumDirEntries);
	FileHeader *mapHdr = new FileHeader;
	FileHeader *dirHdr = new FileHeader;

        DEBUG('f', "Formatting the file system.\n");

    // First, allocate space for FileHeaders for the directory and bitmap
    // (make sure no one else grabs these!)
	freeMap->Mark(FreeMapSector);	    
	freeMap->Mark(DirectorySector);

    // Second, allocate space for the data blocks containing the contents
    // of the directory and bitmap files.  There better be enough space!

	ASSERT(mapHdr->Allocate(freeMap, FreeMapFileSize));
	ASSERT(dirHdr->Allocate(freeMap, DirectoryFileSize));

    // Flush the bitmap and directory FileHeaders back to disk
    // We need to do this before we can "Open" the file, since open
    // reads the file header off of disk (and currently the disk has garbage
    // on it!).

        DEBUG('f', "Writing headers back to disk.\n");
	mapHdr->WriteBack(FreeMapSector);    
	dirHdr->WriteBack(DirectorySector);

    // OK to open the bitmap and directory files now
    // The file system operations assume these two files are left open
    // while Nachos is running.

        freeMapFile = new OpenFile(FreeMapSector);
        directoryFile = new OpenFile(DirectorySector);
     
    // Once we have the files "open", we can write the initial version
    // of each file back to disk.  The directory at this point is completely
    // empty; but the bitmap has been changed to reflect the fact that
    // sectors on the disk have been allocated for the file headers and
    // to hold the file data for the directory and bitmap.

        DEBUG('f', "Writing bitmap and directory back to disk.\n");
	freeMap->WriteBack(freeMapFile);	 // flush changes to disk
	directory->WriteBack(directoryFile);

	if (DebugIsEnabled('f')) {
	    freeMap->Print();
	    directory->Print();

        delete freeMap; 
	delete directory; 
	delete mapHdr; 
	delete dirHdr;
	}
      
    } else {
    // if we are not formatting the disk, just open the files representing
    // the bitmap and directory; these are left open while Nachos is running
        freeMapFile = new OpenFile(FreeMapSector);
        directoryFile = new OpenFile(DirectorySector);
    }

    // 不管 文件系统是不是被格式化
// 创建兑换区 SWAP
    char  swap[5] = {'S','W','A','P','\0'};
    Remove(swap);
    Create(swap,65*128); // 比实际内存大一点
  
    
}

//----------------------------------------------------------------------
// FileSystem::Create
// 	Create a file in the Nachos file system (similar to UNIX create).
//	Since we can't increase the size of files dynamically, we have
//	to give Create the initial size of the file.
//
//	The steps to create a file are:
//	  Make sure the file doesn't already exist
//        Allocate a sector for the file header
// 	  Allocate space on disk for the data blocks for the file
//	  Add the name to the directory
//	  Store the new file header on disk 
//	  Flush the changes to the bitmap and the directory back to disk
//
//	Return TRUE if everything goes ok, otherwise, return FALSE.
//
// 	Create fails if:
//   		file is already in directory
//	 	no free space for file header
//	 	no free entry for file in directory
//	 	no free space for data blocks for the file 
//
// 	Note that this implementation assumes there is no concurrent access
//	to the file system!
//
//	"name" -- name of file to be created
//	"initialSize" -- size of file to be created
//----------------------------------------------------------------------

bool
FileSystem::Create(char *name, int initialSize)
{
  
    Directory *directory;
    BitMap *freeMap;
    FileHeader *hdr;
    int sector;
    bool success;
    DEBUG('f', "FileSystem::Create Creating file %s, size %d\n", name, initialSize);
    int pathSize = 0;
    char **path = splitName(name,&pathSize);
    // 定位到目录
     OpenFile* dirFile = ChangeDir(path,pathSize);
    directory = new Directory(NumDirEntries);
    directory->FetchFrom(dirFile);

    if (directory->Find(path[pathSize-1],FALSE) != -1)
      success = FALSE;			// file is already in directory
    else {	
         
        freeMap = new BitMap(NumSectors);
        freeMap->FetchFrom(freeMapFile);
        sector = freeMap->Find();	// find a sector to hold the file header
    	if (sector == -1) 		
            success = FALSE;		// no free block for file header 
        else if (!directory->Add(path[pathSize-1], sector,FALSE)) // 新建目录项
            success = FALSE;	// no space in directory
        else {
            DEBUG('f', "FileSystem::Create get her\n");
                hdr = new FileHeader;
            if (!hdr->Allocate(freeMap, initialSize))
                    success = FALSE;	// no space on disk for data
            else {	
                success = TRUE;
            // everthing worked, flush all changes back to disk
                    // 写回inode 和 目录 位图
                    hdr->WriteBack(sector); 		
                    directory->WriteBack(dirFile);
                    freeMap->WriteBack(freeMapFile);
            }
                delete hdr;
        }
        delete freeMap;
    }
  
    delete directory;
    return success;
}

//----------------------------------------------------------------------
// FileSystem::Open
// 	Open a file for reading and writing.  
//	To open a file:
//	  Find the location of the file's header, using the directory 
//	  Bring the header into memory
//
//	"name" -- the text name of the file to be opened
//----------------------------------------------------------------------

OpenFile *
FileSystem::Open(char *name)
{ 
    Directory *directory = new Directory(NumDirEntries);
    OpenFile *openFile = NULL;
    
    // 目录位置
    int pathSize = 0;
    int sector;
    char **path = splitName(name,&pathSize);
    // 找到目录
    OpenFile* dirFile = ChangeDir(path,pathSize);
    if(dirFile == nullptr){
        DEBUG('f', "FileSystem::Open Change Directory False\n");
        return nullptr;
    }
    directory->FetchFrom(dirFile);

    DEBUG('f', "Opening file %s\n", path[pathSize-1]);
    directory->FetchFrom(dirFile);
    sector = directory->Find(path[pathSize-1],FALSE); 
    DEBUG('f', "FileSystem::Open sector %d\n",sector);
    if (sector >= 0) 		
	openFile = new OpenFile(sector);	// name was found in directory 
    delete directory;
    return openFile;				// return NULL if not found
}

//----------------------------------------------------------------------
// FileSystem::Remove
// 	Delete a file from the file system.  This requires:
//	    Remove it from the directory
//	    Delete the space for its header
//	    Delete the space for its data blocks
//	    Write changes to directory, bitmap back to disk
//
//	Return TRUE if the file was deleted, FALSE if the file wasn't
//	in the file system.
//
//	"name" -- the text name of the file to be removed
//----------------------------------------------------------------------

bool
FileSystem::Remove(char *name)
{ 

    
    Directory *directory;
    BitMap *freeMap;
    FileHeader *fileHdr;
    int sector;

    int pathSize = 0;
    char **path = splitName(name,&pathSize);
    // 找到目录
    OpenFile* dirFile = ChangeDir(path,pathSize);
    if(dirFile == nullptr){
        DEBUG('a', "FileSystem::Remove Change Directory False\n");
        return FALSE;
    }

    directory = new Directory(NumDirEntries);
    directory->FetchFrom(dirFile);
    
    // 先看他是不是文件
    sector = directory->Find(path[pathSize-1],FALSE);
    if(sector == -1){
        // 看他是不是 文件夹
        sector = directory->Find(path[pathSize-1],TRUE);
         if (sector == -1) {
            DEBUG('a', "FileSystem::Remove sector = -1 not find\n");
            delete directory;
            return FALSE;			 // file not found 
        }
        // 把要删除的目录先打开
        OpenFile* dirToDelete = new OpenFile(sector);
        Directory* directoryToDel = new Directory(NumDirEntries);
        directoryToDel->FetchFrom(dirToDelete);
        ASSERT(directoryToDel->Count()  ==  0);
       
    }
    fileHdr = new FileHeader;
    fileHdr->FetchFrom(sector);

    freeMap = new BitMap(NumSectors);
    freeMap->FetchFrom(freeMapFile);

    fileHdr->Deallocate(freeMap);  		// remove data blocks
    freeMap->Clear(sector);			// remove header block
    // 从目录项中移除
    directory->Remove(path[pathSize-1]);

    freeMap->WriteBack(freeMapFile);		// flush to disk
    directory->WriteBack(dirFile);        // flush to disk
    delete fileHdr;
    delete directory;
    delete freeMap;
    return TRUE;
} 

//----------------------------------------------------------------------
// FileSystem::List
// 	List all the files in the file system directory.
//----------------------------------------------------------------------

void
FileSystem::List()
{
    
    Directory *directory = new Directory(NumDirEntries);

    directory->FetchFrom(directoryFile);
    directory->List();
    delete directory;
}

//----------------------------------------------------------------------
// FileSystem::Print
// 	Print everything about the file system:
//	  the contents of the bitmap
//	  the contents of the directory
//	  for each file in the directory,
//	      the contents of the file header
//	      the data in the file
//----------------------------------------------------------------------

void
FileSystem::Print()
{
    FileHeader *bitHdr = new FileHeader;
    FileHeader *dirHdr = new FileHeader;
    BitMap *freeMap = new BitMap(NumSectors);
    Directory *directory = new Directory(NumDirEntries);

    printf("Bit map file header:\n");
    bitHdr->FetchFrom(FreeMapSector);
    bitHdr->Print();

    printf("Directory file header:\n");
    dirHdr->FetchFrom(DirectorySector);
    dirHdr->Print();

    freeMap->FetchFrom(freeMapFile);
    freeMap->Print();

    directory->FetchFrom(directoryFile);
    directory->Print();

    delete bitHdr;
    delete dirHdr;
    delete freeMap;
    delete directory;
} 


//----------------------------------------------------------------------
// FileSystem::getBitMap
// 	FileSystem 维护了两个打开的文件句柄，因此可以得到镇个系统的BitMap
//----------------------------------------------------------------------
BitMap* FileSystem::getBitMap(){
    BitMap* freeMap = new BitMap(NumSectors);
    freeMap->FetchFrom(freeMapFile);
    return freeMap;
}    

//----------------------------------------------------------------------
// FileSystem::setBitMap
// 	将位图写回磁盘
//----------------------------------------------------------------------

void FileSystem::setBitMap(BitMap* freeMap){
    // 向磁盘写回 BitMap
    freeMap->WriteBack(freeMapFile);
}



//----------------------------------------------------------------------
// FileSystem::splitName
// 	将包含绝对路径的文件名进行分割
//----------------------------------------------------------------------

char** FileSystem::splitName(char* name,int * size){
  
    int begin = 0;
    *size = 1;
    if(name[0] == '/')
        begin = 1;
    else
        begin = 0;

    int p = begin;
    while(name[p] != '\0'){
        if(name[p] == '/')
            (*size)++; // 计算绝对路径中，要分多少段
        p++;
    }
    // 分配空间
    char** path = new char*[*size];
    for(int i = 0;i < *size;i++){
        path[i] = new char[10];
    }
    // 循环将名字填入数组中
    p = begin;
    for(int i = 0;i < *size;i++){
        int j = 0;
        while((name[p]!='/') && (name[p] != '\0')){
            path[i][j] = name[p];
            j++;
            p++;
        }
        path[i][j] = '\0';
        p++;
    }
    
    return path;
}
//----------------------------------------------------------------------
// FileSystem::ChangeDir
// 进入指定目录
//----------------------------------------------------------------------

OpenFile* FileSystem::ChangeDir(char** path,int pathSize){
    Directory *directory  = new Directory(NumDirEntries);
    OpenFile* dirFile = directoryFile;
    int sector;
    // path 有n段 前n-1段是目录路径 
    for(int i = 0;i < pathSize-1;i++){
        DEBUG('f', "FileSystem::ChangeDir cd into dir %s\n", path[i]);
        directory->FetchFrom(dirFile);
        sector = directory->Find(path[i],TRUE);
        DEBUG('f', "FileSystem::ChangeDir sector %d\n", sector);
        if(sector == -1)
            return nullptr; // 没有该目录
        // 传入 inde 所在的块号就能打开文件
        dirFile = new OpenFile(sector);
    }
    return dirFile; // 返回，当前所在的目录的描述符
}
//----------------------------------------------------------------------
// FileSystem::CreateDir
// 	创建文件目录
//----------------------------------------------------------------------

bool FileSystem::CreateDir(char* name){
    Directory* directory  = new Directory(NumDirEntries);
    BitMap *freeMap;
    FileHeader *hdr;
    
    int pathSize = 0;
    int sector;
    bool success;
    char **path = splitName(name,&pathSize);

    
    // 获取 上级目录的文件描述符
    OpenFile* dirFile = ChangeDir(path,pathSize);
    if(dirFile == nullptr){
        DEBUG('f', "FileSystem::CreateDir Change Directory False\n");
        return FALSE;
    }
    directory->FetchFrom(dirFile);
    
    // 在目录中查找文件夹是否已经存在
    if (directory->Find(path[pathSize-1],TRUE) != -1)
      success = FALSE;			// file is already in directory
    else {	
        DEBUG('f', "FileSystem::CreateDir get last directory no exited drictory\n");
        freeMap = new BitMap(NumSectors);
        freeMap->FetchFrom(freeMapFile);
        sector = freeMap->Find();	// find a sector to hold the file header
    	// 找到一空白块，将他分配给新目录的inode
        if (sector == -1) 		
            success = FALSE;		// no free block for file header 
        else if (!directory->Add(path[pathSize-1], sector,TRUE))
            success = FALSE;	// no space in directory
        else {
                hdr = new FileHeader;
            if (!hdr->Allocate(freeMap, DirectoryFileSize))
                    success = FALSE;	// no space on disk for data
            else {	
                success = TRUE;
            // everthing worked, flush all changes back to disk
                    hdr->WriteBack(sector); 		
                    directory->WriteBack(dirFile);
                    freeMap->WriteBack(freeMapFile);
            }
                delete hdr;
        }
        delete freeMap;
    }
    delete directory;
    return success;
}